# -*- coding:utf8 -*-
from django.conf.urls import url

__author__ = 'Forcs'

from threading import Lock, Thread
from abc import abstractmethod, ABCMeta

import json
import datetime

from wanbatvcom.http.whttp import send_error_response_with_message, send_ok_response_with_data

from wancake.models.epgsync.models import EpgSyncRecord

class SyncTask(object):

    def __init__(self, name=None, module_name=None, table_name=None, id_column_name='id'):
        self._host = "218.83.169.39:7080"
        self._api = "epgsync/getepgdata"
        self._table_name = table_name
        self._id_column_name = id_column_name
        self._module_name = module_name
        self._name = name
        if not name:
            self._name = module_name
        self._module_class = getattr(__import__('wancake.models.epgsync.models', {}, {}, ['models']), module_name)

    @property
    def name(self):
        return self._name

    @property
    def module_name(self):
        return self._module_name

    @property
    def module(self):
        return self._module_class

    @property
    def table_name(self):
        return self._table_name

    @property
    def id_column_name(self):
        return self._id_column_name

    def execute(self):
        # step 1: 从EpgSyncRecord表中获取最新的同步记录
        obj, create = EpgSyncRecord.objects.get_or_create(table_name=self._module_name)
        last_id = 0
        if not create:
            last_id = obj.last_id
        # step 2: 从epg服务器中获取更新的数据
        import urllib
        import urllib2
        params = {
            "table": self._table_name,
            "idcolumnname": self._id_column_name,
            "fromid": last_id
        }
        request = urllib2.Request('http://%s/%s?%s' % (self._host, self._api, urllib.urlencode(params)))
        response = urllib2.urlopen(request)
        content = response.read()
        response.close()

        data_list = None
        err = True
        if content:
            result_data = json.loads(content, encoding="utf8")
            if result_data and \
                    result_data["resultCode"] == 1 and \
                    "data" in result_data and \
                    "resultList" in result_data["data"]:

                data_list = result_data["data"]["resultList"]
                err = False

        # step 3: 交由子类处理返回的更新数据
        new_last_id = self.on_load_data(err, data_list, last_id)
        # the end: 如果数据有更新，则刷新EpgSyncRecord表的记录
        if new_last_id > last_id:
            sr = EpgSyncRecord.objects.get(table_name=self._module_name)
            sr.last_id = new_last_id
            sr.column_name = self._id_column_name
            sr.sync_datetime = datetime.datetime.now()
            sr.save()

    @abstractmethod
    def on_load_data(self, err, data_list, last_id):
        pass

class BaseSyncEpgTableTask(SyncTask):

    def __init__(self, module_name=None, table_name=None, id_column_name="id"):
        super(BaseSyncEpgTableTask, self).__init__(module_name=module_name,
                                                   table_name=table_name,
                                                   id_column_name=id_column_name)
        self._current_postion = 0
        self._total_length = 0

    @property
    def current(self):
        return self._current_postion

    @property
    def total(self):
        return self._total_length

    def on_load_data(self, err, data_list, last_id):
        if err:
            return last_id

        length = len(data_list)
        self._total_length = length
        if not length:
            return last_id
        i = 0
        for index, item in enumerate(data_list):
            self._current_postion = index
            try:
                model = getattr(__import__('wancake.models.epgsync.models', {}, {}, ['models']), self.module_name)()
                model.from_json(item, epg_id_column_name=self.id_column_name)
                model.save()
                self.on_save_item(item, model, index, length)
                last_id = item[self.id_column_name]
                i += 1
            except Exception, e:
                pass
        return last_id

    def on_save_item(self, item_data, model_saved, position, total_length):
        pass

class SyncEpgOrderRecordTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgOrderRecordTask, self).__init__(module_name="EpgOrderRecord",
                                                     table_name="order_record",
                                                     id_column_name="order_id")


class SyncEpgStatisCategoryTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgStatisCategoryTask, self).__init__(module_name="EpgStatisCategory",
                                                        table_name="statis_category")


class SyncEpgSysCpProductsTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgSysCpProductsTask, self).__init__(module_name="EpgSysCpProducts",
                                                       table_name="sys_cp_products",
                                                       id_column_name="record_id")


class SyncEpgSysOrganizationTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgSysOrganizationTask, self).__init__(module_name="EpgSysOrganization",
                                                         table_name="sys_organization")


class SyncEpgUserCateStatisTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgUserCateStatisTask, self).__init__(module_name="EpgUserCateStatis",
                                                        table_name="user_cate_statis")


class SyncEpgUserPlayDailyTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgUserPlayDailyTask, self).__init__(module_name="EpgUserPlayDaily",
                                                       table_name="user_play_daily",
                                                       id_column_name="daily_id")


class SyncEpgUserPlayDetailTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgUserPlayDetailTask, self).__init__(module_name="EpgUserPlayDetail",
                                                        table_name="user_play_detail",
                                                        id_column_name="detail_id")


class SyncEpgUserPlayInfoTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgUserPlayInfoTask, self).__init__(module_name="EpgUserPlayInfo",
                                                      table_name="user_play_info")


class SyncEpgUserPlayTotalTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgUserPlayTotalTask, self).__init__(module_name="EpgUserPlayTotal",
                                                       table_name="user_play_total")


class SyncEpgWanbaDailyOrderStatTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgWanbaDailyOrderStatTask, self).__init__(module_name="EpgWanbaDailyOrderStat",
                                                             table_name="wanba_daily_order_stat",
                                                             id_column_name="record_id")


class SyncEpgWanbaDailyStatisTask(BaseSyncEpgTableTask):

    def __init__(self):
        super(SyncEpgWanbaDailyStatisTask, self).__init__(module_name="EpgWanbaDailyStatis",
                                                          table_name="wanba_daily_statis",
                                                          id_column_name="record_id")
SYNC_TASK_LIST = [
    SyncEpgOrderRecordTask,
    SyncEpgStatisCategoryTask,
    SyncEpgSysCpProductsTask,
    SyncEpgSysOrganizationTask,
    SyncEpgUserCateStatisTask,
    SyncEpgUserPlayDailyTask,
    SyncEpgUserPlayDetailTask,
    SyncEpgUserPlayInfoTask,
    SyncEpgUserPlayTotalTask,
    SyncEpgWanbaDailyOrderStatTask,
    SyncEpgWanbaDailyStatisTask
]

class EpgSyncer(Thread):

    def __init__(self, callback=None):
        super(EpgSyncer, self).__init__(name='epg_syncer')
        self._callback = callback
        self._current_task = None

    def current(self):
        if self._current_task:
            return '%d / %d' % (self._current_task.current, self._current_task.total)
        else:
            return 'none'

    def run(self):
        if self._callback and hasattr(self._callback, "on_start") and callable(getattr(self._callback, "on_start")):
            getattr(self._callback, "on_start")()

        for task_class in SYNC_TASK_LIST:
            task = task_class()
            self._current_task = task

            if self._callback and hasattr(self._callback, "on_execute_task") and callable(getattr(self._callback, "on_execute_task")):
                getattr(self._callback, "on_execute_task")(task)
            task.execute()
            if self._callback and hasattr(self._callback, "on_done_task") and callable(getattr(self._callback, "on_done_task")):
                getattr(self._callback, "on_done_task")(task)
            self._current_task = None

        if self._callback and hasattr(self._callback, "on_finish") and callable(getattr(self._callback, "on_finish")):
            getattr(self._callback, "on_finish")()

class EpgSyncService(object):

    STATE_IDLE = 1
    STATE_SYNCING = 2
    STATE_UNKNOWN = -1

    def __init__(self):
        self._sync_state = EpgSyncService.STATE_UNKNOWN
        self._lock = Lock()
        self._syncer = None

    def is_syncing(self):
        """
        :return: 是否正在执行同步任务
        """
        return self._sync_state == EpgSyncService.STATE_SYNCING

    def execute_sync(self, request):
        """
        执行同步
        :param request:
        :return:
        """
        if self.is_syncing():
            return send_error_response_with_message(message=u'正在执行同步任务')
        else:
            if self._lock.acquire():
                if not self._syncer:
                    self._syncer = EpgSyncer(self)
                self._syncer.start()
                self._lock.release()

            return send_ok_response_with_data(data={
                "syncing": True
            })

    def on_start(self):
        self._sync_state = EpgSyncService.STATE_SYNCING

    def on_finish(self):
        self._sync_state = EpgSyncService.STATE_IDLE

        if self._lock.acquire():
            self._syncer = None
            self._lock.release()

    def on_execute_task(self, task):
        if task:
            print 'execute task:%s, state:%d' % (task.name, self._sync_state)

    def on_done_task(self, task):
        if task:
            print 'done task:%s, state:%d' % (task.name, self._sync_state)

    @property
    def urls(self):
        urlpatterns = [
            url('^epgsyncer', self.execute_sync, name="sync_epg"),
            url('^csv/orderrecord$', self.import_order_record_csv),
        ]
        return urlpatterns

    def import_order_record_csv(self, request):
        form_files = request.FILES
        try:
            csv_file = form_files["file"]
        except KeyError as e:
            return send_error_response_with_message(message=str(e))

        if not csv_file:
            return send_error_response_with_message(message='CSV文件无效')

        import csv
        csv_reader = csv.reader(csv_file)
        FIELD_ROW = 0
        row = 0
        fields = list()
        csv_list = list()
        for csv_row in csv_reader:
            csv_cols = csv_row
            if not csv_cols[0]:
                continue

            if row == FIELD_ROW:
                for csv_col in csv_cols:
                    if csv_col:
                        fields.append(csv_col)
                row += 1
                continue

            fields_length = len(fields)
            row_info = dict()
            for index, value in enumerate(fields):
                field_name = fields[index]
                if index < fields_length:
                    row_info[field_name] = csv_cols[index].decode("gb2312").encode("utf8")
                    if field_name == 'CHARGING_START' or field_name == 'CHARGING_END':
                        row_info[field_name] = datetime.datetime.strptime(row_info[field_name], "%Y/%m/%d %H:%M")
            csv_list.append(row_info)
            row += 1

        from wancake.models.epgsync.models import EpgOrderRecord
        try:
            order_record_qs = EpgOrderRecord.objects.all()
            total = len(csv_list)
            suc_count = 0
            fail_count = 0
            for order_record_item in order_record_qs:

                if not csv_list:
                    break

                try:
                    for csv_row_info in csv_list:
                        csv_charging_start_time = csv_row_info["CHARGING_START"].strftime("%Y/%m/%d %H:%M")
                        csv_charging_start_time_secondary = (csv_row_info["CHARGING_START"] - datetime.timedelta(minutes=1))\
                            .strftime("%Y/%m/%d %H:%M")
                        subscribe_time = order_record_item.subscribe_time
                        if order_record_item.user_id.find(csv_row_info["USERID"]) > -1 \
                            and (csv_charging_start_time == subscribe_time.strftime("%Y/%m/%d %H:%M")
                                 or csv_charging_start_time_secondary == subscribe_time.strftime("%Y/%m/%d %H:%M")
                                 or csv_charging_start_time ==
                                    (subscribe_time + datetime.timedelta(hours=8)).strftime("%Y/%m/%d %H:%M")
                                 or csv_charging_start_time_secondary ==
                                    (subscribe_time + datetime.timedelta(hours=8)).strftime("%Y/%m/%d %H:%M")):
                            order_record_item.unsubscribe_time = csv_row_info["CHARGING_END"]
                            order_record_item.save()
                            suc_count += 1
                            csv_list.remove(csv_row_info)
                            break

                except Exception:
                    fail_count += 1

        except Exception as e:
            return send_error_response_with_message(message=str(e))

        return send_ok_response_with_data(data={
            "updateCount": suc_count,
            "errorCount": fail_count,
            "total": total
        })


epg_sync = EpgSyncService()
